/**
 * Copyright 2009 Jesse Brown (jbrown@fluentv.com)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package com.fluentv.games.netblocks;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.util.Log;

/**
 * Manages and plays sound effects for various kinds of ingame actions.
 * 
 * @author Jesse Brown (jbrown@fluentv.com)
 */
public class SoundEffectsPlayer {
  
  private static final String LOG_TAG = SoundEffectsPlayer.class.getSimpleName(); 
  private static final ExecutorService asyncFxService = Executors.newSingleThreadExecutor();
  private final Context context;  
  private final AtomicBoolean muted = new AtomicBoolean(false);
  
  public SoundEffectsPlayer(Context context) {
    this.context = context;
  }
  
  /**
   * Mute
   */
  public void mute() {
    muted.set(true);
  }
  
  /**
   * Unmute
   */
  public void unMute() {
    muted.set(false);
  }
  
  /**
   * Block moved successfully.
   */
  public void moveBlockSuccess() {
    playSoundEffect(R.raw.plick);
  }
  
  /**
   * Timed block move down
   */
  public void timedMoveBlock() {
    playSoundEffect(R.raw.synthdr);
  }
  
  /**
   * New block
   */
  public void newBlock() {
    playSoundEffect(R.raw.dinkdink);
  }
  
  /**
   * Block hit an 'edge' and could not move. 
   */
  public void moveBlockFailed() {
    playSoundEffect(R.raw.chink);
  }
  
  /**
   * Block rotation succeeded
   */
  public void rotateBlockSuccess() {
    playSoundEffect(R.raw.plick);
  }
  
  /**
   * Block rotation failed.
   */
  public void rotateBlockFailed() {
    playSoundEffect(R.raw.wrump);  
  }
  
  /**
   * Solved a row
   */
  public void solvedRow() {
    playSoundEffect(R.raw.boyoit);
  }
  
  /**
   * Started playing a level
   */
  public void startLevel() {
    playSoundEffect(R.raw.doublebeep);
  }

  /**
   * Drops a block
   */
  public void dropBlock() {
    playSoundEffect(R.raw.chaclunk);
  }
  
  /**
   * Lost the game
   */
  public void lostGame() {
    playSoundEffect(R.raw.uhoh);
  }
  
  /**
   * Play the specified sound effect at 100% volume (adjusted for user volume).
   * 
   * If muted, does nothing.
   */
  private void playSoundEffect(int resource) {
    if (muted.get()) {
      return;
    }
    
    // Put a new event on our event queue to play in the background.
    asyncFxService.submit(new AsyncSoundPlayer(context, resource));
  }
    
  /**
   * Simple class to wrap up playing a single specific sound effect.
   * Allows all the initialization and playback to occur on a different 
   * thread than the UI event. 
   */
  private static class AsyncSoundPlayer implements Runnable {
    
    // A single on completion listener (thread safe) to clean up 
    // the finished sound effect objects.
    private static final OnCompletionListener CLEANUP_LISTENER = new OnCompletionListener() {
      @Override
      public void onCompletion(MediaPlayer mp) {
        mp.release();      
      }    
    };
    
    // A simple error listener to log errors that occur.
    private static final OnErrorListener ERROR_LISTENER = new OnErrorListener() {
      @Override
      public boolean onError(MediaPlayer mp, int what, int extra) {
        Log.e(LOG_TAG, "Error recieved for " + mp.getCurrentPosition() + " what " + what + " extra " + extra);
        return true;
      }    
    };  
        
    private final int resourceId;
    private final Context context;
    
    public AsyncSoundPlayer(Context context, int resourceId) {
      this.context = context;
      this.resourceId = resourceId;
    }
    
    @Override
    public void run() {           
      MediaPlayer newSound = MediaPlayer.create(context, resourceId);
      newSound.setOnCompletionListener(CLEANUP_LISTENER);    
      newSound.setOnErrorListener(ERROR_LISTENER);           
      newSound.start();   
    }    
  }
}
